use libc::CLOCK_REALTIME;

use crate::{
    atomic::mi_atomic_add,
    heap::mi_heap_get_default,
    init::_mi_stats_main,
    types::{mi_stat_count_t, mi_stat_counter_t, mi_stats_t},
};

static mut mi_time_start: f64 = 0.0;

pub unsafe fn mi_stats_get_default() -> *mut mi_stats_t {
    let heap = mi_heap_get_default();
    let heap = &mut *heap;
    let tld = &mut *heap.tld;
    &mut tld.stats
}

pub unsafe fn mi_stats_reset() {
    let stats = mi_stats_get_default();
    if stats as *const _ != &_mi_stats_main {
        *stats = core::mem::zeroed();
    }
    _mi_stats_main = core::mem::zeroed();
    mi_time_start = mi_clock_start();
}

static mut mi_clock_diff: f64 = 0.0;

pub unsafe fn mi_clock_start() -> f64 {
    if mi_clock_diff == 0.0 {
        let t0 = mi_clock_now();
        mi_clock_diff = mi_clock_now() - t0;
    }
    mi_clock_now()
}

pub unsafe fn mi_clock_now() -> f64 {
    let mut t = core::mem::zeroed();
    libc::clock_gettime(CLOCK_REALTIME, &mut t);
    t.tv_sec as f64 + 1.0e-9 * t.tv_nsec as f64
}

pub unsafe fn mi_stat_increase(stat: *mut mi_stat_count_t, mount: usize) {
    mi_stat_update(stat, mount as i64);
}

pub unsafe fn mi_stat_decrease(stat: *mut mi_stat_count_t, mount: usize) {
    mi_stat_update(stat, -(mount as i64));
}

pub unsafe fn mi_stat_update(stat: *mut mi_stat_count_t, amount: i64) {
    if amount == 0 {
        return;
    }

    let in_main = ((stat as usize) >= (&_mi_stats_main as *const _ as usize))
        && ((stat as usize) < ((&_mi_stats_main as *const _ as usize) + (size_of::<mi_stats_t>())));

    if in_main {
        // add atomically (for abandoned pages)
        let current = mi_atomic_add(&mut (&mut *stat).current, amount);
        if current > (&mut *stat).peak {
            (&mut *stat).peak = (&mut *stat).current;
        }

        if amount > 0 {
            mi_atomic_add(&mut (&mut *stat).allocated, amount);
        } else {
            mi_atomic_add(&mut (&mut *stat).freed, -amount);
        }
    } else {
        let stat = &mut *stat;
        stat.current += amount;
        if stat.current > stat.peak {
            stat.peak = stat.current;
        }

        if amount > 0 {
            stat.allocated += amount
        } else {
            stat.freed += -amount
        }
    }
}

pub unsafe fn mi_stat_counter_increase(stat: *mut mi_stat_counter_t, amount: usize) {
    mi_atomic_add(&mut (&mut *stat).count, amount as i64);
}
